package main

import "fmt"

// Eval evaluates a Scheme expression in the given environment
func Eval(expr *Value, env *Environment) (*Value, error) {
	switch expr.Type {
	case TypeInteger, TypeRational, TypeReal, TypeComplex, TypeBoolean, TypeString, TypeChar, TypePair, TypeVector:
		// Self-evaluating expressions
		return expr, nil

	case TypeSymbol:
		// Variable lookup
		return env.Get(expr.Symbol)

	case TypeList:
		if len(expr.List) == 0 {
			return NewNil(), nil
		}

		// Check for special forms
		first := expr.List[0]
		if first.Type == TypeSymbol {
			switch first.Symbol {
			case "quote":
				return evalQuote(expr.List[1:], env)
			case "define":
				return evalDefine(expr.List[1:], env)
			case "set!":
				return evalSet(expr.List[1:], env)
			case "lambda":
				return evalLambda(expr.List[1:], env)
			case "if":
				return evalIf(expr.List[1:], env)
			case "cond":
				return evalCond(expr.List[1:], env)
			case "let":
				return evalLet(expr.List[1:], env)
			case "begin":
				return evalBegin(expr.List[1:], env)
			}
		}

		// Function application
		return evalApplication(expr.List, env)

	default:
		return nil, fmt.Errorf("cannot evaluate expression of type %v", expr.Type)
	}
}

// evalSet handles set! special form - updates the value of an existing binding
func evalSet(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("set! expects exactly 2 arguments")
	}

	name := args[0]
	value := args[1]

	if name.Type != TypeSymbol {
		return nil, fmt.Errorf("set! first argument must be a symbol")
	}

	// Evaluate the value expression
	evalValue, err := Eval(value, env)
	if err != nil {
		return nil, err
	}

	// Update the binding
	err = env.Set(name.Symbol, evalValue)
	if err != nil {
		return nil, err
	}

	// Return the value that was set (Scheme convention)
	return evalValue, nil
}

// evalQuote handles quote special form
func evalQuote(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 1 {
		return nil, fmt.Errorf("quote expects exactly 1 argument")
	}
	return args[0], nil
}

// evalDefine handles define special form
func evalDefine(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("define expects exactly 2 arguments")
	}

	first := args[0]
	second := args[1]

	// Check if first argument is a list (function definition shorthand)
	if first.Type == TypeList && len(first.List) > 0 {
		// Function definition: (define (name param1 param2 ...) body)
		functionName := first.List[0]
		if functionName.Type != TypeSymbol {
			return nil, fmt.Errorf("function name must be a symbol")
		}

		// Extract parameters
		var paramNames []string
		for _, param := range first.List[1:] {
			if param.Type != TypeSymbol {
				return nil, fmt.Errorf("function parameters must be symbols")
			}
			paramNames = append(paramNames, param.Symbol)
		}

		// Create lambda function
		lambdaFn := NewFunction(paramNames, second, env)
		env.Define(functionName.Symbol, lambdaFn)
		return NewSymbol(functionName.Symbol), nil
	} else if first.Type == TypeSymbol {
		// Variable definition: (define name value)
		value, err := Eval(second, env)
		if err != nil {
			return nil, err
		}

		env.Define(first.Symbol, value)
		return NewSymbol(first.Symbol), nil
	} else {
		return nil, fmt.Errorf("define expects a symbol or function definition as first argument")
	}
}

// evalLambda handles lambda special form
func evalLambda(args []*Value, env *Environment) (*Value, error) {
	if len(args) != 2 {
		return nil, fmt.Errorf("lambda expects exactly 2 arguments")
	}

	params := args[0]
	body := args[1]

	if params.Type != TypeList {
		return nil, fmt.Errorf("lambda expects a list of parameters")
	}

	var paramNames []string
	for _, param := range params.List {
		if param.Type != TypeSymbol {
			return nil, fmt.Errorf("lambda parameters must be symbols")
		}
		paramNames = append(paramNames, param.Symbol)
	}

	return NewFunction(paramNames, body, env), nil
}

// evalIf handles if special form
func evalIf(args []*Value, env *Environment) (*Value, error) {
	if len(args) < 2 || len(args) > 3 {
		return nil, fmt.Errorf("if expects 2 or 3 arguments")
	}

	condition, err := Eval(args[0], env)
	if err != nil {
		return nil, err
	}

	if condition.IsTruthy() {
		return Eval(args[1], env)
	} else if len(args) == 3 {
		return Eval(args[2], env)
	} else {
		return NewNil(), nil
	}
}

// evalCond handles cond special form
func evalCond(args []*Value, env *Environment) (*Value, error) {
	for _, clause := range args {
		if clause.Type != TypeList || len(clause.List) != 2 {
			return nil, fmt.Errorf("cond clauses must be lists of length 2")
		}

		condition := clause.List[0]
		result := clause.List[1]

		// Check for else clause
		if condition.Type == TypeSymbol && condition.Symbol == "else" {
			return Eval(result, env)
		}

		condValue, err := Eval(condition, env)
		if err != nil {
			return nil, err
		}

		if condValue.IsTruthy() {
			return Eval(result, env)
		}
	}

	return NewNil(), nil
}

// evalLet handles let special form
func evalLet(args []*Value, env *Environment) (*Value, error) {
	// Let should have at least 2 arguments: bindings and body
	if len(args) < 2 {
		return nil, fmt.Errorf("let expects at least 2 arguments")
	}

	bindings := args[0]

	if bindings.Type != TypeList {
		return nil, fmt.Errorf("let bindings must be a list")
	}

	// Create new environment for let scope
	letEnv := NewEnvironment(env)

	// Process bindings
	for _, binding := range bindings.List {
		if binding.Type != TypeList || len(binding.List) != 2 {
			return nil, fmt.Errorf("let bindings must be lists of length 2")
		}

		name := binding.List[0]
		value := binding.List[1]

		if name.Type != TypeSymbol {
			return nil, fmt.Errorf("let binding names must be symbols")
		}

		evalValue, err := Eval(value, env) // Evaluate in outer environment
		if err != nil {
			return nil, err
		}

		letEnv.Define(name.Symbol, evalValue)
	}

	// Process body expressions
	var result *Value
	var err error
		i := 1
	for i < len(args) {
		// Check if current expression is a define
		if expr := args[i]; expr.Type == TypeList && len(expr.List) > 0 && 
			expr.List[0].Type == TypeSymbol && expr.List[0].Symbol == "define" {
			// Evaluate define in let environment
			if _, err := evalDefine(expr.List[1:], letEnv); err != nil {
				return nil, err
			}
			i++
		} else {
			// No more defines, start evaluating body expressions
			break
		}
	}

	// If we processed all expressions, return nil
	if i >= len(args) {
		return NewNil(), nil
	}

	// Evaluate the remaining expressions sequentially
	for ; i < len(args); i++ {
		result, err = Eval(args[i], letEnv)
		if err != nil {
			return nil, err
		}
	}

	return result, nil
}

// evalBegin handles begin special form - evaluates expressions sequentially
func evalBegin(args []*Value, env *Environment) (*Value, error) {
	if len(args) == 0 {
		return NewNil(), nil
	}

	var result *Value
	var err error

	// Evaluate all expressions, return the last one
	for _, expr := range args {
		result, err = Eval(expr, env)
		if err != nil {
			return nil, err
		}
	}

	return result, nil
}

// evalApplication handles function application
func evalApplication(exprs []*Value, env *Environment) (*Value, error) {
	if len(exprs) == 0 {
		return NewNil(), nil
	}

	// Evaluate function
	fn, err := Eval(exprs[0], env)
	if err != nil {
		return nil, err
	}

	// Evaluate arguments
	var args []*Value
	for _, arg := range exprs[1:] {
		evalArg, err := Eval(arg, env)
		if err != nil {
			return nil, err
		}
		args = append(args, evalArg)
	}

	// Apply function
	switch fn.Type {
	case TypeBuiltin:
		return fn.Builtin(args, env)

	case TypeFunction:
		if len(args) != len(fn.Function.Parameters) {
			return nil, fmt.Errorf("function expects %d arguments, got %d",
				len(fn.Function.Parameters), len(args))
		}

		// Create new environment for function scope
		fnEnv := NewEnvironment(fn.Function.Closure)

		// Bind parameters to arguments
		for i, param := range fn.Function.Parameters {
			fnEnv.Define(param, args[i])
		}

		// Evaluate function body
		return Eval(fn.Function.Body, fnEnv)

	default:
		return nil, fmt.Errorf("cannot apply %v as function", fn.Type)
	}
}

// EvalString parses and evaluates a string input
func EvalString(input string, env *Environment) (*Value, error) {
	// 1. Tokenize the entire input
	tokens, err := Tokenize(input)
	if err != nil {
		return nil, err
	}

	// 2. Create a parser
	parser := NewParser(tokens)

	// 3. Define result variables
	var lastResult *Value = nil
	var parseErr error = nil

	// 4. Loop to parse and evaluate all expressions until EOF or error
	for {
		// Try to parse the next expression
		expr, err := parser.parseExpression()
		if err != nil {
			// If it's an EOF error, all expressions have been processed
			if err.Error() == "unexpected end of input" {
				break
			}
			parseErr = err
			break
		}

		// Evaluate the expression
		result, evalErr := EvalWithTCO(expr, env)
		if evalErr != nil {
			return nil, evalErr
		}

		// Save the result of the last expression
		lastResult = result

		// Skip whitespace (if any)
		if parser.pos < len(parser.tokens) {
			token := parser.current()
			// Check if there are more non-EOF tokens
			if token.Type == TokenEOF {
				break
			}
		}
	}

	// If there's a parsing error, return it
	if parseErr != nil {
		return nil, parseErr
	}

	// Return the result of the last expression
	return lastResult, nil
}
